home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 2004 #11
/
Amiga Plus CD - 2004 - No. 11.iso
/
AmiSoft
/
Game
/
misc
/
WormWars.lha
/
WormWars
/
Source
/
engine1.c
< prev
next >
Wrap
C/C++ Source or Header
|
2004-08-22
|
93KB
|
2,785 lines
// 1. INCLUDES -----------------------------------------------------------
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE */
#include "stdafx.h"
#include "diff.h"
#include "same.h"
#include "engine.h"
/* Rules for variable types:
SBYTE is used for field coordinates and queue indices
UBYTE is used for contents
SWORD is used for frequencies
ULONG is used for scores */
// 2. DEFINES ------------------------------------------------------------
// 3. EXPORTED VARIABLES -------------------------------------------------
ABOOL anims = TRUE,
banging = FALSE,
clearthem = FALSE,
enclosed = FALSE,
engraved = TRUE,
modified = FALSE,
randomflag = FALSE,
randomarray[MAXLEVELS + 1],
turbo = FALSE;
UBYTE board[MAXLEVELS + 1][MINFIELDX + 1][MINFIELDY + 1],
engraving,
field[MAXFIELDX + 1][MAXFIELDY + 1],
tailfield[MAXFIELDX + 1][MAXFIELDY + 1],
ice;
SBYTE a = GAMEOVER,
players,
level = 1, levels, reallevel, sourcelevel,
numberx, numbery, number,
treasurer,
startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
SWORD fieldx, fieldy,
secondsleft, secondsperlevel;
TEXT pathname[81],
date[DATELENGTH + 1],
times[TIMELENGTH + 1],
stattext[7 + 1];
ULONG delay, r;
ULONG quantity[5][3];
AGLOBAL struct HiScoreStruct hiscore[HISCORES + 1];
AGLOBAL struct WormStruct worm[4];
AGLOBAL struct TeleportStruct teleport[2];
AGLOBAL struct ProtectorStruct protector[4][PROTECTORS + 1];
AGLOBAL struct MagnetStruct magnet[MAGNETS + 1];
AGLOBAL struct CreatureStruct creature[CREATURES + 1];
AGLOBAL struct ObjectStruct object[LASTOBJECT + 1] =
{ {2310, 30}, // AFFIXER
{ 300, 30}, // AMMO
{ 510, 30}, // ARMOUR
{1325, 30}, // AUTOJUMP
{ 410, 30}, // BONUS
{1040, 30}, // BRAKES
{1000, 30}, // CONVERTER
{ 880, 30}, // CUTTER
{ 870, 30}, // CYCLONE_O
{ 900, 30}, // ENCLOSER
{ 305, 30}, // GLOW
{ 555, 30}, // GROWER
{1600, 30}, // ICE
{ 600, 30}, // LIGHTNING
{1600, 30}, // MAGNET
{ 250, 30}, // MINIBOMB
{ 900, 30}, // MINIHEALER
{ 680, 30}, // MISSILE_O
{1100, 30}, // MULTIPLIER
{ 745, 30}, // POWER
{ 890, 30}, // PROTECTOR
{ 660, 30}, // PULSE
{ 875, 30}, // PUSHER
{1000, 30}, // REMNANT
{1200, 30}, // SIDESHOT
{1300, 30}, // SLAYER
{1600, 30}, // SLOWER
{ 410, 30}, // SUPERBOMB
{7100, 30}, // SUPERHEALER
{1690, 30}, // SWITCHER
{3900, 30}, // TREASURE
{6200, 30} // UMBRELLA
};
AGLOBAL struct CreatureInfoStruct creatureinfo[SPECIES + 1] =
{ { ANT, FALSE, 310, 17, 13, 50}, // 0
{ BIRD, FALSE, 500, 25, 12, 50}, // 1
{ BULL, FALSE, 640 ,30, 3, 50}, // 2
{ CAMEL, FALSE, 1090, 45, 16, 100}, // 3
{ CLOUD, FALSE, 300, 22, 8, 50}, // 4
{ CYCLONE_C, FALSE, 0, 0, 1, 100}, // 5
{ DOG, FALSE, 350, 12, 3, 50}, // 6
{ FISH, TRUE, 400, 32, 9, 50}, // 7
{ FRAGMENT, FALSE, 0, 0, 3, 0}, // 8
{ FROG, FALSE, 600, 27, 4, 50}, // 9
{ GIRAFFE, FALSE, 365, 7, 255, 50}, // 10
{ KANGAROO, FALSE, 750, 10, 9, 100}, // 11
{ MISSILE_C, FALSE, 0, 0, 6, 50}, // 12
{ MONKEY, FALSE, 470, 40, 7, 100}, // 13
{ MOUSE, FALSE, 290, 32, 9, 50}, // 14
{ OCTOPUS, TRUE, 450, 15, 14, 50}, // 15
{ ORB, FALSE, 195, 5, 5, 50}, // 16
{ OTTER, TRUE, 0, 0, 12, 100}, // 17
{ RABBIT, FALSE, 1060, 35, 3, 200}, // 18
{ RAIN, FALSE, 0, 0, 4, 50}, // 19
{ SALAMANDER, FALSE, 320, 20, 9, 50}, // 20
{ SNAIL, FALSE, 260, 0, 50, 50}, // 21
{ SPIDER, FALSE, 850, 0, 17, 50}, // 22
{ GOOSE, FALSE, 990, 42, 25, 100}, // 23
{ HORSE, FALSE, 500, 37, 7, 50}, // 24
{ BEAR, TRUE, 450, 47, 10, 50}, // 25
{ ELEPHANT, FALSE, 500, 9, 18, 50} // 26
};
// 4. IMPORTED VARIABLES -------------------------------------------------
// 5. MODULE VARIABLES ---------------------------------------------------
MODULE ABOOL teleports,
trainer;
MODULE UBYTE infector[MAXFIELDX + 1][MAXFIELDY + 1];
MODULE TEXT timedisplay[5];
// 6. MODULE STRUCTURES --------------------------------------------------
MODULE UBYTE eachworm[4][2][9] =
{ { { GREENHEAD_NW, GREENHEADUP, GREENHEAD_NE,
GREENHEADLEFT, ANYTHING, GREENHEADRIGHT,
GREENHEAD_SW, GREENHEADDOWN, GREENHEAD_SE
},
{ GREENGLOW_NW, GREENGLOWUP, GREENGLOW_NE,
GREENGLOWLEFT, ANYTHING, GREENGLOWRIGHT,
GREENGLOW_SW, GREENGLOWDOWN, GREENGLOW_SE
} },
{ { REDHEAD_NW, REDHEADUP, REDHEAD_NE,
REDHEADLEFT, ANYTHING, REDHEADRIGHT,
REDHEAD_SW, REDHEADDOWN, REDHEAD_SE
},
{ REDGLOW_NW, REDGLOWUP, REDGLOW_NE,
REDGLOWLEFT, ANYTHING, REDGLOWRIGHT,
REDGLOW_SW, REDGLOWDOWN, REDGLOW_SE
} },
{ { BLUEHEAD_NW, BLUEHEADUP, BLUEHEAD_NE,
BLUEHEADLEFT, ANYTHING, BLUEHEADRIGHT,
BLUEHEAD_SW, BLUEHEADDOWN, BLUEHEAD_SE
},
{ BLUEGLOW_NW, BLUEGLOWUP, BLUEGLOW_NE,
BLUEGLOWLEFT, ANYTHING, BLUEGLOWRIGHT,
BLUEGLOW_SW, BLUEGLOWDOWN, BLUEGLOW_SE
} },
{ { YELLOWHEAD_NW, YELLOWHEADUP, YELLOWHEAD_NE,
YELLOWHEADLEFT,ANYTHING, YELLOWHEADRIGHT,
YELLOWHEAD_SW, YELLOWHEADDOWN,YELLOWHEAD_SE
},
{ YELLOWGLOW_NW, YELLOWGLOWUP, YELLOWGLOW_NE,
YELLOWGLOWLEFT,ANYTHING, YELLOWGLOWRIGHT,
YELLOWGLOW_SW, YELLOWGLOWDOWN,YELLOWGLOW_SE
} } };
// 7. MODULE FUNCTIONS ---------------------------------------------------
MODULE void death(void);
MODULE void fastloop(void);
MODULE void killall(void);
MODULE void magnetloop(void);
MODULE void newhiscores(void);
MODULE void slowloop(void);
MODULE void ReadGameports(void);
MODULE void endoflevel(void);
MODULE void drawcause(SBYTE player, SBYTE state);
MODULE void newlevel(UBYTE player);
MODULE void ramming(SBYTE player);
MODULE void wormworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2);
MODULE SWORD atleast(SWORD value, SWORD minimum);
MODULE ABOOL findempty(SBYTE* x, SBYTE* y, ABOOL mode);
MODULE SBYTE slowdown(SBYTE speed, FLAG isworm, FLAG brakes);
MODULE FLAG checkautojump(SBYTE player);
// 8. CODE ---------------------------------------------------------------
AGLOBAL ABOOL blockedtel(UBYTE which, SBYTE deltax, SBYTE deltay)
{ if (blockedsquare(xwrap(teleport[partner(which)].x + deltax), ywrap(teleport[partner(which)].y + deltay)))
{ return TRUE;
} else
{ return FALSE;
} }
AGLOBAL void bombblast(UBYTE triggerer, SBYTE player, SBYTE centrex, SBYTE centrey, FLAG superbomb)
{ SBYTE counter, strength,
uppy, uppymax,
downy, downymax,
leftx, leftxmax,
rightx, rightxmax,
x, y;
effect(FXUSE_BOMB);
if (superbomb)
{ strength = ADD_SUPERBOMB + arand(RAND_SUPERBOMB);
} else
{ strength = ADD_MINIBOMB + arand(RAND_MINIBOMB );
}
leftxmax = centrex - strength;
if (leftxmax < 0)
leftxmax = 0;
rightxmax = centrex + strength;
if (rightxmax > fieldx)
rightxmax = fieldx;
uppymax = centrey - strength;
if (uppymax < 0)
uppymax = 0;
downymax = centrey + strength;
if (downymax > fieldy)
downymax = fieldy;
leftx = centrex;
rightx = centrex;
uppy = centrey;
downy = centrey;
for (counter = 1; counter <= strength; counter++)
{ if (leftx > leftxmax)
{ leftx--;
for (y = uppy; y <= downy; y++)
squareblast(triggerer, player, field[leftx][y], leftx, y, FALSE, superbomb);
}
if (uppy > uppymax)
{ uppy--;
for (x = leftx; x <= rightx; x++)
squareblast(triggerer, player, field[x][uppy], x, uppy, FALSE, superbomb);
}
if (rightx < rightxmax)
{ rightx++;
for (y = downy; y >= uppy; y--)
squareblast(triggerer, player, field[rightx][y], rightx, y, FALSE, superbomb);
}
if (downy < downymax)
{ downy++;
for (x = rightx; x >= leftx; x--)
squareblast(triggerer, player, field[x][downy], x, downy, FALSE, superbomb);
} } }
AGLOBAL SBYTE bsign(SBYTE value)
{ if (value < 0)
return (-1);
elif (value > 0)
return (1);
else return (0);
}
AGLOBAL void changefield(void)
{ SBYTE x, y;
if (randomflag && a == PLAYGAME && level)
{ do
{ sourcelevel = arand(levels - 1) + 1;
} while (randomarray[sourcelevel]);
randomarray[sourcelevel] = TRUE;
} else sourcelevel = level;
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
field[x][y] = EMPTY; // slow method
for (x = 0; x <= MINFIELDX; x++)
for (y = 0; y <= MINFIELDY; y++)
field[x + LEFTGAP][y + TOPGAP] = board[sourcelevel][x][y];
}
void clearhiscores(void)
{ SBYTE i;
clearthem = FALSE;
for (i = 0; i <= HISCORES; i++)
{ hiscore[i].player = -1;
hiscore[i].level = 0;
hiscore[i].score = 0;
hiscore[i].fresh = FALSE;
hiscore[i].name[0] = 0;
hiscore[i].time[0] = 0;
hiscore[i].date[0] = 0;
} }
MODULE void death(void)
{ SBYTE pain, player;
UBYTE which;
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ if (!worm[player].alive)
{ pain = 0;
if (worm[player].cause >= FIRSTTAIL && worm[player].cause <= LASTTAIL)
{ if (player == worm[player].cause - FIRSTTAIL)
pain = PAIN_FRIENDLYTAIL;
else pain = PAIN_ENEMYTAIL;
} elif (worm[player].cause >= FIRSTGLOW && worm[player].cause <= LASTGLOW)
{ pain = PAIN_GLOW;
} elif (worm[player].cause >= FIRSTFIRE && worm[player].cause <= LASTFIRE)
pain = PAIN_WORMFIRE;
elif (worm[player].cause >= FIRSTHEAD && worm[player].cause <= LASTHEAD)
pain = PAIN_HEAD;
elif (worm[player].cause >= FIRSTPROTECTOR && worm[player].cause <= LASTPROTECTOR)
pain = PAIN_PROTECTOR;
elif
( worm[player].cause >= FIRSTCREATURE
|| (worm[player].cause >= FIRSTRAIN && worm[player].cause <= LASTRAIN )
|| (worm[player].cause >= FIRSTMISSILE && worm[player].cause <= LASTMISSILE)
)
{ pain = PAIN_CREATURE;
} else switch (worm[player].cause)
{
case METAL:
pain = PAIN_METAL;
break;
case SLIME:
pain = PAIN_SLIME;
break;
case STONE:
pain = PAIN_STONE;
break;
case TELEPORT:
pain = PAIN_TELEPORT;
break;
case WOOD:
pain = PAIN_WOOD;
break;
case MINIBOMB:
case SUPERBOMB:
pain = PAIN_BOMB;
break;
case SLAYER:
pain = PAIN_SLAYER;
break;
default:
// assert(0);
break;
}
if (pain > worm[player].lives)
worm[player].lives = 0;
else worm[player].lives -= pain;
draw(worm[player].x, worm[player].y, FIRSTPAIN + player);
drawcause(player, NORMAL);
stat(player, MINIHEALER);
if (level)
worm[player].levelreached = level;
else worm[player].levelreached = reallevel;
if (worm[player].lives)
{ effect(FXPAIN + player);
worm[player].alive = TRUE;
worm[player].causewait = r + CAUSEWAIT;
} else
{ /* kill worm */
effect(FXDEATH_WORM);
change(worm[player].x, worm[player].y, FIRSTGRAVE + player);
updatearrow(worm[player].y);
for (which = 0; which <= PROTECTORS; which++)
if (protector[player][which].alive && protector[player][which].visible)
change(protector[player][which].x, protector[player][which].y, EMPTY);
for (which = 0; which <= MAGNETS; which++)
if (magnet[which].player == player)
magnet[which].alive = FALSE;
if (worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
} } } }
if (!worm[0].lives && !worm[1].lives && !worm[2].lives && !worm[3].lives)
{ /* End of game */
for (player = 0; player <= 3; player++)
if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
newhiscores();
effect(FXGAMEOVER);
playsong(1);
a = GAMEOVER;
say((STRPTR) "Game over!", WHITE);
waitasec();
anykey(FALSE);
} }
MODULE void drawcause(SBYTE player, SBYTE state)
{ if (state == BLACK)
{ draw
( -6,
(player * 10) + 7,
BLACKENED
);
} else
{ draw
( -6,
(player * 10) + 7,
worm[player].cause
);
} }
/* NAME enginesetup -- once-only initialization of engine variables
SYNOPSIS enginesetup(void);
FUNCTION Sets up the unchanging worm variables.
MODULE engine.c */
void enginesetup(void)
{ worm[0].statx = worm[0].staty = worm[1].staty = worm[2].statx = 0;
worm[1].statx = worm[2].staty = worm[3].statx = worm[3].staty = 1;
worm[0].colour = GREEN;
worm[1].colour = RED;
worm[2].colour = BLUE;
worm[3].colour = YELLOW;
worm[0].dimcolour = DARKGREEN;
worm[1].dimcolour = DARKRED;
worm[2].dimcolour = DARKBLUE;
worm[3].dimcolour = DARKYELLOW;
worm[0].port = 2;
worm[1].port = 3;
worm[2].port = 1;
worm[3].port = 0;
worm[0].name[0] =
worm[1].name[0] =
worm[2].name[0] =
worm[3].name[0] = 0;
strcpy(pathname, DEFAULTSET);
engraving = 0; // you can't use rand() in this function, it is called too early in the startup by SAS/C.
systemsetup();
}
MODULE void fastloop(void)
{ SBYTE i, player, x, y;
// decrement score if stopped
for (player = 0; player <= 3; player++)
{ if
( worm[player].lives
&& worm[player].speed == 0
&& worm[player].score >= worm[player].multi * players
)
{ wormscore(player, -1);
} }
// animate
if (anims)
{ for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].visible)
{ if (creature[i].species == BIRD)
{ if (!(r % ANIMSPEED_BIRD))
{ creature[i].frame += creature[i].dir;
drawcreature(i);
if (creature[i].frame == 0)
{ creature[i].dir = 1;
} elif (creature[i].frame == 4)
{ creature[i].dir = -1;
} } }
elif (creature[i].species == MISSILE_C)
{ if (!(r % ANIMSPEED_MISSILE))
{ drawcreature(i);
if (++creature[i].frame > MISSILEFRAMES)
{ creature[i].frame = 0;
} } }
elif (creature[i].species == FRAGMENT && creature[i].subspecies == BANANA)
{ if (!(r % ANIMSPEED_BANANA))
{ if (creature[i].frame == 0 && creature[i].dir == -1)
{ creature[i].frame = 7;
} elif (creature[i].frame == 7 && creature[i].dir == 1)
{ creature[i].frame = 0;
} else creature[i].frame += creature[i].dir;
drawcreature(i);
} } } } }
if (banging)
{ banging = FALSE;
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
if (field[x][y] == BANGDYNAMITE)
{ change(x, y, SILVER);
bangdynamite(x, y, infector[x][y]);
}
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
if (field[x][y] == TEMPBANGDYNAMITE)
{ banging = TRUE;
field[x][y] = BANGDYNAMITE;
} }
// flash letter
if (level)
{ if (r % 8 == 1)
{ draw(numberx, numbery, WHITENED);
} elif (r % 8 == 2)
{ draw(numberx, numbery, FIRSTNUMBER + number - 1);
} }
// flash icons
for (player = 0; player <= 3; player++)
{ if (worm[player].control != NONE)
{ icon(player, GLOW);
icon(player, CUTTER);
icon(player, ARMOUR);
} } }
MODULE ABOOL findempty(SBYTE* x, SBYTE* y, ABOOL mode)
{ SBYTE xx, yy;
UBYTE c;
if (mode)
{ do
{ xx = arand(fieldx);
yy = arand(fieldy);
c = field[xx][yy];
} while
( c != SLIME
&& c != WOOD
&& c != STONE
&& c != METAL
&& (c < FIRSTTAIL || c > LASTTAIL)
); // theoretical chance of infinite loop here
} else
{ do
{ xx = arand(fieldx);
yy = arand(fieldy);
c = field[xx][yy];
} while (c < FIRSTEMPTY || c > LASTEMPTY); // theoretical chance of infinite loop here
}
*x = xx;
*y = yy;
return(TRUE);
}
void gameloop(void)
{ SBYTE i, player;
if (a == PLAYGAME)
{ ReadGameports();
fastloop();
gameinput();
}
if (a == PLAYGAME)
{ ReadGameports();
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ if (worm[player].speed)
{ if (!(r % worm[player].speed))
{ if (worm[player].wait)
{ worm[player].wait = FALSE;
} else wormloop(player);
} }
else
{ stoppedwormloop(player);
} } } }
if (a == PLAYGAME && !ice)
{ ReadGameports();
for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive)
{ if (creature[i].species == BULL && creature[i].deltax == 0)
{ for (player = 0; player <= 3; player++)
{ if (worm[player].lives && worm[player].y == creature[i].y)
{ if (creature[i].dir == -1)
{ if (worm[player].x < creature[i].x)
{ creature[i].deltax = -1;
} }
else
{ // assert(creature[i].dir == 1);
if (worm[player].x > creature[i].x)
{ creature[i].deltax = 1;
} } } } }
if (!(r % creature[i].speed))
{ creatureloop(i);
} } } }
if (a == PLAYGAME)
{ ReadGameports();
if (!(r % SPEED_MAGNET))
{ magnetloop();
}
death();
}
if (a == PLAYGAME)
{ ReadGameports();
if (!(r % VERYSLOW))
{ slowloop();
} } }
MODULE void killall(void)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
creature[i].alive = FALSE;
for (i = 0; i <= MAGNETS; i++)
magnet[i].alive = FALSE;
teleports = FALSE;
}
SBYTE loadfields(STRPTR fieldname)
{ SBYTE i, j;
UBYTE IOBuffer[NAMELENGTH + 1];
/* This routine is not entirely robust, especially regarding
failures part way through reading. It is very trusting of its data;
ie. it doesn't do any sanity checking.
Provided that the fieldset was created with Worm Wars's built-in field
editor, and the file is not corrupt, these failures should never
happen anyway.
open file */
// say("Opening...", WHITE);
if (!ZOpen(fieldname, FALSE))
return 1; /* no harm done */
/* read header
say("Reading header...", WHITE);
FSET 7.4#!
0123456789
# is the NULL terminator
! is levels */
if (!ZRead(IOBuffer, 10))
{ ZClose();
return 2; /* no harm done */
}
if (strcmp((STRPTR) IOBuffer, "FSET 7.5"))
{ ZClose();
return 3; /* no harm done */
}
levels = IOBuffer[9];
/* read high score table */
// say("Reading high score table...", WHITE);
for (i = 0; i <= HISCORES; i++)
{ if (!ZRead(IOBuffer, 6))
{ ZClose();
return 4; /* incorrect levels */
}
hiscore[i].fresh = FALSE;
hiscore[i].player = IOBuffer[0];
hiscore[i].level = IOBuffer[1];
hiscore[i].score = (IOBuffer[2] * 16777216)
+ (IOBuffer[3] * 65536)
+ (IOBuffer[4] * 256)
+ IOBuffer[5];
if (!ZRead(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return 5; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= NAMELENGTH; j++)
{ hiscore[i].name[j] = IOBuffer[j];
}
if (!ZRead(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return 6; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= TIMELENGTH; j++)
{ hiscore[i].time[j] = IOBuffer[j];
}
if (!ZRead(IOBuffer, DATELENGTH + 1))
{ ZClose();
return 7; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= DATELENGTH; j++)
{ hiscore[i].date[j] = IOBuffer[j];
} }
// say("Reading level data...", WHITE);
/* read level data */
for (i = 0; i <= levels; i++)
{ if (!ZRead(IOBuffer, 8))
{ ZClose();
return 9;
/* incorrect levels, corrupted high scores,
incorrect startx, field data */
}
startx[i] = IOBuffer[0];
starty[i] = IOBuffer[1];
if (!ZRead((UBYTE *) &board[i][0][0], LEVELSIZE))
{ ZClose();
return 10;
/* incorrect levels, corrupted high scores,
incorrect startx, field data */
} }
// say("Open done.", WHITE);
// no need to read version string
ZClose();
modified = FALSE;
return 0;
}
MODULE void magnetloop(void)
{ SBYTE i;
UBYTE c;
for (i = 0; i <= MAGNETS; i++)
{ if (magnet[i].alive)
{ // ensure magnet is still valid
if (field[magnet[i].x][magnet[i].y] != magnet[i].object)
{ magnet[i].alive = FALSE;
} else
{ change(magnet[i].x, magnet[i].y, EMPTY);
magnet[i].x += bsign(worm[magnet[i].player].x - magnet[i].x);
magnet[i].y += bsign(worm[magnet[i].player].y - magnet[i].y);
c = field[magnet[i].x][magnet[i].y];
if
( (c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
|| (c >= FIRSTGLOW && c <= LASTGLOW)
)
{ change(magnet[i].x, magnet[i].y, magnet[i].object);
} elif (c >= FIRSTHEAD && c <= LASTHEAD)
{ change(magnet[i].x, magnet[i].y, magnet[i].object);
wormscore(c - FIRSTHEAD, wormobject(c - FIRSTHEAD, magnet[i].x, magnet[i].y));
drawhead(magnet[i].player);
} else magnet[i].alive = FALSE;
} } } }
AGLOBAL void newfield(void)
{ SBYTE x, y;
startx[level] = CENTREX;
starty[level] = CENTREY;
if (level)
{ for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ board[level][x][y] = EMPTY;
} } }
else
{ for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ board[0][x][y] = SILVER;
} } } }
void newfields(void)
{ if (verify())
{ strcpy(pathname, DEFAULTSET);
levels = DEFAULTLEVELS;
modified = FALSE;
for (level = 0; level <= levels; level++)
newfield();
clearhiscores();
level = 1;
if (a == FIELDEDIT)
{ turborender();
saylevel(WHITE);
} else hiscores();
} }
void newgame(void)
{ SBYTE i, player;
players = 0;
for (player = 0; player <= 3; player++)
{ if (worm[player].control != NONE)
players++;
worm[player].lives = 0;
worm[player].speed = NORMAL;
worm[player].hiscore = 0;
}
for (i = 1; i <= MAXLEVELS; i++)
{ randomarray[i] = FALSE;
}
r = -1;
trainer =
turbo = FALSE;
ice = 0;
reallevel = 0;
level = 1;
a = PLAYGAME;
clearscreen();
newlevel(arand(3));
timing();
}
MODULE void newhiscores(void)
{ PERSIST TEXT amiganame[4][NAMELENGTH + 1] = {"Jay Miner", "Carl Sassenrath", "R. J. Mical", "Dave Morse"};
AUTO SBYTE i, j, player;
datestamp();
for (player = 0; player <= 3; player++)
{ for (i = 0; i <= HISCORES; i++)
{ if (worm[player].control != NONE && worm[player].score >= hiscore[i].score)
{ /* push all worse hiscores down */
if (i < HISCORES)
{ for (j = HISCORES; j >= i + 1; j--)
{ hiscore[j].player = hiscore[j - 1].player;
hiscore[j].level = hiscore[j - 1].level;
hiscore[j].score = hiscore[j - 1].score;
hiscore[j].fresh = hiscore[j - 1].fresh;
strcpy(hiscore[j].name, hiscore[j - 1].name);
strcpy(hiscore[j].date, hiscore[j - 1].date);
strcpy(hiscore[j].time, hiscore[j - 1].time);
} }
modified = TRUE;
hiscore[i].player = player;
hiscore[i].level = worm[player].levelreached;
hiscore[i].score = worm[player].hiscore;
if (worm[player].control == AMIGA)
{ strcpy(hiscore[i].name, amiganame[player]);
hiscore[i].fresh = FALSE;
} else
{ strcpy(hiscore[i].name, "(New)");
hiscore[i].fresh = TRUE;
}
strcpy(hiscore[i].time, times);
strcpy(hiscore[i].date, date);
break; /* vital */
} } } }
MODULE void newlevel(UBYTE player)
{ SBYTE delays, i, j;
if (level >= 2)
enginerundown(player); // enginerundown() could easily be incorporated directly into newlevel()
if (a == PLAYGAME)
{ say("", BLACK);
if (engraving == 8)
{ engraving = 0;
} else
{ engraving++;
}
if (level > levels)
{ for (i = 0; i <= 3; i++)
{ if (worm[i].lives)
worm[i].levelreached = -1;
if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
}
celebrate();
newhiscores();
} else
{ saylevel(WHITE);
for (i = 0; i <= 3; i++)
{ if (worm[i].multi > 1)
{ worm[i].multi /= 2;
} }
killall();
turborender();
if (level == 0)
{ playsong(3);
} else
{ playsong(4 + (level % 3));
}
for (i = 0; i <= 3; i++)
{ worm[i].speed = NORMAL;
if (worm[i].lives)
stat(i, BRAKES);
worm[i].moved = FALSE;
worm[i].numbers = 0;
worm[i].x = startx[sourcelevel] + LEFTGAP;
worm[i].y = starty[sourcelevel] + TOPGAP;
worm[i].arrowy = worm[i].y;
switch(i)
{
case 0:
worm[0].deltax = -1;
worm[0].deltay = 0;
break;
case 1:
worm[1].deltax = 1;
worm[1].deltay = 0;
break;
case 2:
worm[2].deltax = 0;
worm[2].deltay = -1;
break;
case 3:
worm[3].deltax = 0;
worm[3].deltay = 1;
break;
default:
break;
} }
if
( (worm[0].control != NONE && worm[0].control != AMIGA)
|| (worm[1].control != NONE && worm[1].control != AMIGA)
|| (worm[2].control != NONE && worm[2].control != AMIGA)
|| (worm[3].control != NONE && worm[3].control != AMIGA)
)
{ turbo = FALSE;
}
delay = DELAY;
delays = level / 5;
if (delays)
{ for (i = 1; i <= delays; i++)
{ delay = delay * 19 / 20;
} }
if (level)
{ secondsperlevel = SECONDSPERLEVEL;
number = 1;
putnumber();
for (i = 0; i <= 3; i++)
{ if (!worm[i].lives && worm[i].control != NONE)
{ /* create (or resurrect) a worm */
worm[i].lives = STARTLIVES;
worm[i].alive = TRUE;
worm[i].multi = 1;
worm[i].causewait = (ULONG) -1;
worm[i].last = FIRSTTAIL + i;
worm[i].nextlife = LIFEMODULO;
worm[i].pos = -1;
worm[i].remnants =
worm[i].affixer =
worm[i].sideshot =
worm[i].pusher =
worm[i].flashed =
worm[i].encloser =
worm[i].rammed =
worm[i].frosted =
worm[i].wait =
worm[i].autojump =
worm[i].brakes = FALSE;
worm[i].score =
worm[i].oldscore =
worm[i].armour =
worm[i].power =
worm[i].ammo =
worm[i].glow =
worm[i].cutter =
worm[i].olddeltax =
worm[i].olddeltay = 0;
for (j = 0; j <= PROTECTORS; j++)
{ protector[i][j].alive = FALSE;
}
for (j = 0; j <= LASTOBJECT; j++)
{ stat(i, j);
} } } }
for (i = 0; i <= 3; i++)
{ if (worm[i].control != NONE)
{ for (j = 0; j <= LASTOBJECT; j++)
{ icon(i, j);
} } } } }
stopfx();
clearkybd();
resettime();
}
AGLOBAL SBYTE partner(SBYTE which)
{ if (which % 2 == 0)
return((SBYTE) (which + 1));
else return((SBYTE) (which - 1));
}
AGLOBAL void putnumber(void)
{ SBYTE oldnumbery = numbery;
ABOOL done;
do
{ done = findempty(&numberx, &numbery, FALSE);
} while (!done);
change(numberx, numbery, FIRSTNUMBER + number - 1);
updatearrow(oldnumbery);
updatearrow(numbery);
}
AGLOBAL void reflect(UBYTE which)
{ creature[which].deltax = -creature[which].deltax;
creature[which].deltay = -creature[which].deltay;
creature[which].x += creature[which].deltax * 2;
creature[which].y += creature[which].deltay * 2;
}
ABOOL savefields(STRPTR fieldname)
{ SBYTE i, j;
UBYTE IOBuffer[NAMELENGTH + 1];
if (!ZOpen(fieldname, TRUE))
{ return FALSE;
}
/* write header */
strcpy((STRPTR) IOBuffer, "FSET 7.5");
IOBuffer[9] = levels;
if (!ZWrite(IOBuffer, 10))
{ ZClose();
return FALSE;
}
/* write high score table */
for (i = 0; i <= HISCORES; i++)
{ IOBuffer[0] = hiscore[i].player;
IOBuffer[1] = hiscore[i].level;
IOBuffer[2] = hiscore[i].score / 16777216;
IOBuffer[3] = (hiscore[i].score % 16777216) / 65536;
IOBuffer[4] = ((hiscore[i].score % 16777216) % 65536) / 256;
IOBuffer[5] = ((hiscore[i].score % 16777216) % 65536) % 256;
if (!ZWrite(IOBuffer, 6))
{ ZClose();
return FALSE;
}
for (j = 0; j <= NAMELENGTH; j++)
IOBuffer[j] = hiscore[i].name[j];
if (!ZWrite(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= TIMELENGTH; j++)
IOBuffer[j] = hiscore[i].time[j];
if (!ZWrite(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= DATELENGTH; j++)
IOBuffer[j] = hiscore[i].date[j];
if (!ZWrite(IOBuffer, DATELENGTH + 1))
{ ZClose();
return FALSE;
} }
/* write level data */
for (i = 0; i <= levels; i++)
{ IOBuffer[0] = startx[i];
IOBuffer[1] = starty[i];
IOBuffer[2] =
IOBuffer[3] =
IOBuffer[4] =
IOBuffer[5] =
IOBuffer[6] =
IOBuffer[7] = 0;
if (!ZWrite(IOBuffer, 8))
{ ZClose();
return FALSE;
}
if (!ZWrite((UBYTE *) &board[i][0][0], LEVELSIZE))
{ ZClose();
return FALSE;
} }
/* write version string */
if (!ZWrite((UBYTE *) VERSION, strlen(VERSION)))
{ ZClose();
return FALSE;
}
ZClose();
if (clearthem)
clearhiscores();
modified = FALSE;
return TRUE;
}
void saylevel(COLOUR colour)
{ TEXT mainstring[15] = "Level ", tempstring[4];
if (level > 0)
{ stci_d(&mainstring[6], level);
strcat((char*) mainstring, " of ");
stci_d(tempstring, levels);
strcat((char*) mainstring, (char*) tempstring);
say(mainstring, colour);
} else
{ say("Bonus Level", colour);
} }
MODULE SBYTE slowdown(SBYTE speed, FLAG isworm, FLAG brakes)
{ speed *= 2;
if (isworm && speed > SLOW)
{ if (brakes)
{ speed = 0;
} else speed = SLOW;
}
return(speed);
}
MODULE void slowloop(void)
{ SBYTE i, j, player, which, x, xx, y, yy;
UBYTE c;
ABOOL done, ok;
UWORD chance;
if (ice)
{ ice--;
}
/* decrement worm strength */
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ if (worm[player].cutter > 0)
{ worm[player].cutter--;
icon(player, CUTTER);
}
if (worm[player].glow > 0)
{ worm[player].glow--;
icon(player, GLOW);
}
if (worm[player].armour > 0)
{ worm[player].armour--;
icon(player, ARMOUR);
} } }
/* blank out old causes */
for (player = 0; player <= 3; player++)
{ if (worm[player].lives > 0 && r > worm[player].causewait)
{ drawcause(player, BLACK);
worm[player].causewait = (ULONG) -1; /* most future time possible */
} }
if (!ice)
{ if (level)
{ for (i = 0; i <= SPECIES; i++)
{ if (creatureinfo[i].freq == 0)
{ continue;
}
// assert(creatureinfo[i].doublelevel != 0);
if (level >= creatureinfo[i].doublelevel)
{ chance = creatureinfo[i].freq / 2;
} else chance = creatureinfo[i].freq;
if (!arand(chance))
{ switch(creatureinfo[i].symbol)
{
case BIRD:
if (findempty(&x, &y, creatureinfo[i].wall))
{ // check whether this location is far enough away from the worms
ok = TRUE;
for (j = 0; j <= 3; j++)
{ if
( worm[j].lives
&& abs(worm[j].x - x) < DISTANCE_BIRD * 2
&& abs(worm[j].y - y) < DISTANCE_BIRD * 2
)
{ ok = FALSE;
break;
} }
if (ok)
{ for (j = 0; j <= CREATURES; j++)
{ if (!creature[j].alive)
{ createcreature(BIRD, j, x, y, 0, 0, 255, 0);
break;
} } } }
break;
case CLOUD:
do
{ x = arand(fieldx - 10) + 5;
y = arand(fieldy / 2);
c = field[x][y];
} while (c < FIRSTEMPTY || c > LASTEMPTY);
for (j = 0; j <= CREATURES; j++)
{ if (!creature[j].alive)
{ if (x < fieldx / 2)
{ createcreature(CLOUD, j, x, y, -1, 0, 255, 0);
} else
{ createcreature(CLOUD, j, x, y, 1, 0, 255, 0);
}
break;
} }
break;
case RABBIT:
if (!arand(1))
{ x = 0;
xx = 1;
} else
{ x = fieldx;
xx = -1;
}
y = arand(fieldy);
done = FALSE;
do
{ if (!valid(x, y))
{ done = TRUE;
} elif
( field[x][y] <= LASTOBJECT
|| (field[x][y] >= FIRSTEMPTY && field[x][y] <= LASTEMPTY)
)
{ for (j = 0; j <= CREATURES; j++)
{ if (!creature[j].alive)
{ createcreature(RABBIT, j, x, y, xx, 0, 255, 0);
break;
} }
done = TRUE;
} else
{ x += xx;
} }
while (!done);
break;
default:
if (findempty(&x, &y, creatureinfo[i].wall))
{ for (j = 0; j <= CREATURES; j++)
{ if (!creature[j].alive)
{ createcreature(creatureinfo[i].symbol, j, x, y, 0, 0, 255, 0);
break;
} } }
break;
} // end switch
} // end if
} // end for
/* create slime */
if (!arand(FREQ_SLIME) && findempty(&x, &y, FALSE))
change(x, y, SLIME);
/* grow slime */
if (!arand(FREQ_SLIMEGROW))
{ for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ if (field[x][y] == SLIME)
{ for (xx = x - 1; xx <= x + 1; xx++)
{ for (yy = y - 1; yy <= y + 1; yy++)
{ if (valid(xx, yy) && field[xx][yy] == EMPTY && !arand(1))
{ field[xx][yy] = TEMPSLIME;
} } } } } }
for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ if (field[x][y] == TEMPSLIME)
{ change(x, y, SLIME);
} } } } }
/* create objects */
for (which = 0; which <= LASTOBJECT; which++)
{ if (level || which == TREASURE)
{ if (!arand(object[which].freq) && findempty(&x, &y, FALSE))
{ change(x, y, which);
} }
elif
( which != TREASURE
&& which != UMBRELLA
&& (!arand(object[which].freq / 5))
&& findempty(&x, &y, FALSE)
)
{ change(x, y, which);
} }
/* create teleports */
if
( !arand(FREQ_TELEPORT)
&& !teleports
&& findempty(&(teleport[0].x), &(teleport[0].y), FALSE)
&& findempty(&(teleport[1].x), &(teleport[1].y), FALSE)
&& (teleport[0].x != teleport[1].x || teleport[0].y != teleport[1].y)
)
{ teleports = TRUE;
change(teleport[0].x, teleport[0].y, TELEPORT);
change(teleport[1].x, teleport[1].y, TELEPORT);
} } }
AGLOBAL SBYTE speedup(SBYTE speed, FLAG isworm)
{ if (speed == 0)
{ speed = SLOW;
} elif (speed >= 2)
{ speed /= 2;
}
if (isworm)
{ if (speed < FAST)
{ speed = FAST;
} }
return(speed);
}
AGLOBAL void squareblast(SBYTE type, SBYTE player, UBYTE c, SBYTE x, SBYTE y, ABOOL cutter, FLAG superbomb)
{ SBYTE which;
UBYTE filler;
if (superbomb)
{ // assert(!cutter);
filler = DYNAMITE;
} else filler = EMPTY;
if (c <= LASTOBJECT)
{ if (!cutter)
{ change(x, y, filler);
} }
elif
( (c >= FIRSTTAIL && c <= LASTTAIL)
|| c == WOOD
|| c == SLIME
|| (superbomb && c >= FIRSTGLOW && c <= LASTGLOW)
)
{ change(x, y, filler);
} elif (c >= FIRSTHEAD && c <= LASTHEAD)
{ if (!cutter && (type != HEAD || player != c - FIRSTHEAD))
{ if (worm[c - FIRSTHEAD].armour == 0)
{ if (superbomb)
{ worm[c - FIRSTHEAD].cause = SUPERBOMB;
} else
{ worm[c - FIRSTHEAD].cause = MINIBOMB;
}
worm[c - FIRSTHEAD].alive = FALSE;
} else
{ effect(FXUSE_ARMOUR);
} } }
elif (c == MISSILE_C)
{ which = whichcreature(x, y, MISSILE_C, 255);
if (!cutter)
{ if (type == HEAD)
{ if (player != c - FIRSTMISSILE)
{ wormkillcreature(player, which);
change(x, y, filler);
} } }
else
{ creature[which].alive = FALSE;
change(x, y, filler);
} }
elif (c >= FIRSTCREATURE && !cutter)
{ which = whichcreature(x, y, c, 255);
if (creature[which].player != player)
{ if (type == HEAD)
{ wormkillcreature(player, which);
change(x, y, FIRSTFLOWER + player);
} else
{ creature[which].alive = FALSE;
change(x, y, filler);
} } } }
void timeloop(void)
{ AUTO UBYTE i, players = 0;
AUTO SBYTE x, y;
PERSIST ABOOL expired = FALSE;
secondsleft = atleast(secondsleft, 0);
timedisplay[0] = 48 + (secondsleft / 60);
timedisplay[1] = ':';
timedisplay[2] = 48 + ((secondsleft % 60) / 10);
timedisplay[3] = 48 + ((secondsleft % 60) % 10);
timedisplay[4] = 0;
if (!level)
{ say(timedisplay, worm[treasurer].colour);
if (!secondsleft)
{ level = reallevel + 1;
secondsleft = SECONDSPERLEVEL;
newlevel(treasurer);
} }
elif (!secondsleft)
{ if (!expired)
{ effect(FXSIREN);
say("Time expired!", WHITE);
expired = TRUE;
for (i = 0; i <= 3; i++)
{ if (worm[i].control != NONE)
{ players++;
} }
// assert(players >= 1 && players <= 4);
for (i = 1; i <= players; i++)
{ do
{ x = (arand(fieldx - 10)) + 5; // not too close to x-edges
y = arand(fieldy);
} while (field[x][y] < FIRSTEMPTY || field[x][y] > LASTEMPTY); // theoretical chance of infinite loop here
change(x, y, START);
}
// create otters
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ for (y = 0; y <= fieldy - 1; y++)
{ if (field[0][y] >= FIRSTEMPTY && field[0][y] <= LASTEMPTY)
{ createcreature(OTTER, i, 0, y, 0, 1, 255, 0);
break;
} }
break;
} }
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ for (y = fieldy; y >= 1; y--)
{ if (field[fieldx][y] >= FIRSTEMPTY && field[fieldx][y] <= LASTEMPTY)
{ createcreature(OTTER, i, fieldx, y, 0, -1, 255, 0);
break;
} }
break;
} } } }
else
{ if (secondsleft <= 5)
{ effect(FXSIREN);
}
expired = FALSE;
say(timedisplay, WHITE);
} }
void train(SCANCODE scancode, FLAG shift)
{ SBYTE x, y;
UBYTE i;
switch(scancode)
{
case HELP:
trainer = ~trainer;
break;
case NUMERICSLASH:
/* Complete the level. */
if (trainer)
{ trainer = FALSE;
endoflevel();
}
break;
case NUMERICASTERISK:
/* field[][] dump, for debugging purposes. */
if (trainer)
{ trainer = FALSE;
say("Xenia rules!", PURPLE);
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
draw(x, y, field[x][y]);
anykey(FALSE);
}
break;
case NUMERICMINUS:
if (trainer)
{ trainer = FALSE;
say("Tiyla rules!", PURPLE);
for (i = 0; i <= ARRAYSIZE; i++)
{ draw(fieldx - 15 + (i % 16), i / 16, i);
}
if (shift)
{ for (i = 0; i <= TAILARRAYSIZE; i++)
{ drawtail(fieldx - 27 + (i % 28), (i / 28) + 16, i);
} }
else
{ for (i = 0; i <= TAILARRAYSIZE; i++)
{ drawtail(fieldx - 15 + (i % 16), (i / 16) + 16, i);
} }
for (i = 0; i <= HEADARRAYSIZE; i++)
{ realdrawhead(fieldx - 15 + (i % 16), (i / 16) + 32, i);
}
anykey(FALSE);
}
break;
case NUMERICPLUS:
if (trainer)
{ trainer = FALSE;
if (worm[1].lives)
{ say("Trainer activated!", PURPLE);
anykey(FALSE);
worm[1].lives = LIVESLIMIT;
stat(1, MINIHEALER);
worm[1].ammo = AMMOLIMIT;
stat(1, AMMO);
worm[1].power = POWERLIMIT;
stat(1, POWER);
worm[1].brakes = TRUE;
stat(1, BRAKES);
worm[1].affixer = TRUE;
worm[1].remnants = TRUE;
worm[1].sideshot = TRUE;
worm[1].pusher = TRUE;
worm[1].encloser = TRUE;
worm[1].autojump = TRUE;
worm[1].armour = ARMOURLIMIT;
worm[1].glow = GLOWLIMIT;
worm[1].cutter = CUTTERLIMIT;
for (i = 0; i <= LASTOBJECT; i++)
{ icon(1, i);
}
trainer = FALSE;
} }
break;
default:
break;
} }
AGLOBAL void turnworm(SBYTE player, SBYTE deltax, SBYTE deltay)
{ if (worm[player].deltax == deltax && worm[player].deltay == deltay)
{ worm[player].speed = speedup(worm[player].speed, TRUE);
stat(player, BRAKES);
} elif (worm[player].deltax == -deltax && worm[player].deltay == -deltay)
{ worm[player].speed = slowdown(worm[player].speed, TRUE, worm[player].brakes);
stat(player, BRAKES);
} else
{ worm[player].deltax = deltax;
worm[player].deltay = deltay;
} }
SBYTE valid(SBYTE x, SBYTE y)
{ if (x < 0 || y < 0)
{ return(FALSE);
} elif (a == FIELDEDIT)
{ if (x > MINFIELDX || y > MINFIELDY)
{ return(FALSE);
} else return(TRUE);
} else
{ // assert(a != FIELDEDIT);
if (x > fieldx || y > fieldy)
{ return(FALSE);
} else return(TRUE);
} }
AGLOBAL void wormscore(SBYTE player, SLONG score)
{ worm[player].score += score * worm[player].multi * players;
stat(player, BONUS);
}
SBYTE xwrap(SBYTE x)
{ if (x < 0)
x += fieldx + 1;
elif (x > fieldx)
x -= fieldx + 1;
return(x);
}
SBYTE ywrap(SBYTE y)
{ if (y < 0)
y += fieldy + 1;
elif (y > fieldy)
y -= fieldy + 1;
return(y);
}
MODULE void ramming(SBYTE player)
{ SBYTE i;
worm[player].rammed = TRUE;
worm[player].x = xwrap(worm[player].x - worm[player].deltax);
worm[player].y = ywrap(worm[player].y - worm[player].deltay);
for (i = 0; i <= PROTECTORS; i++)
{ /* no point checking whether the protectors are alive or dead */
protector[player][i].x -= worm[player].deltax;
protector[player][i].y -= worm[player].deltay;
} }
MODULE SWORD atleast(SWORD value, SWORD minimum)
{ if (value < minimum)
return(minimum);
else return(value);
}
AGLOBAL void __inline change(SBYTE x, SBYTE y, UBYTE image)
{ // assert(valid(x, y));
field[x][y] = image;
draw(x, y, image);
}
/* WormWars FSET format for fieldset contents and high score table (Amiga
and IBM-PC), as follows:
header
TEXT[] "FSET x.x" (NULL-terminated)
SBYTE levels;
high score table
for (slot = 0; slot <= HISCORES; slot++)
{ SBYTE hiscore[slot].player,
hiscore[slot].level;
SLONG hiscore[slot].score;
TEXT[] hiscore[slot].name (NULL-terminated)
TEXT[] hiscore[slot].time (NULL-terminated)
TEXT[] hiscore[slot].date (NULL-terminated)
}
level data
for (level = 0; level <= levels; level++)
{ SBYTE startx[level],
starty[level];
UBYTE reserved[6];
for (x = 0; x <= MINFIELDX; x++)
for (y = 0; y <= MINFIELDY; y++)
SBYTE board[level][x][y];
}
version string
TEXT[] "$VER: Worm Wars x.x (dd.mm.yy) $" (NULL-terminated) */
MODULE void wormworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].armour == 0 && worm[which2].armour == 0)
{ /* both worms die */
worm[which1].cause = FIRSTHEAD + which2;
worm[which1].alive = FALSE;
worm[which2].cause = FIRSTHEAD + which1;
worm[which2].alive = FALSE;
} elif (worm[which1].armour > 0 && worm[which2].armour == 0)
{ /* 1st worm lives, 2nd worm dies */
worm[which2].cause = FIRSTHEAD + which1;
worm[which2].alive = FALSE;
} elif (worm[which1].armour == 0 && worm[which2].armour > 0)
{ /* 1st worm dies, 2nd worm lives */
worm[which1].cause = FIRSTHEAD + which2;
worm[which1].alive = FALSE;
} }
AGLOBAL void protworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ SBYTE i, j = -1; // to prevent spurious warnings
for (i = 0; i <= PROTECTORS; i++)
if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
{ j = i;
break;
}
if (which1 != which2)
{ if (worm[which2].armour == 0)
{ effect(FXBORN_PROTECTOR);
worm[which2].cause = FIRSTPROTECTOR + which1;
worm[which2].alive = FALSE;
} else
{ effect(FXUSE_ARMOUR);
protector[which1][j].visible = FALSE;
} }
else
{ /* protector is over worm's own head; caused by ramming */
protector[which1][j].visible = FALSE;
} }
AGLOBAL void protprot(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ SBYTE i, p1 = -1, p2 = -1; // to prevent spurious warnings
/* Find both protectors */
for (i = 0; i <= PROTECTORS; i++)
{ if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
p1 = i;
if (protector[which2][i].alive && protector[which2][i].x == x && protector[which2][i].y == y)
p2 = i;
}
protector[which1][p1].alive = FALSE;
protector[which2][p2].alive = FALSE;
change(x, y, EMPTY);
}
void icon(SBYTE player, UBYTE image)
{ SBYTE x = -4, y = (player * 10) + 6;
switch(image)
{
// booleans
case AFFIXER:
if (worm[player].affixer)
draw(x, y, AFFIXER);
else draw(x, y, BLACKENED);
break;
case AUTOJUMP:
if (worm[player].autojump)
draw(x + 1, y, AUTOJUMP);
else draw(x + 1, y, BLACKENED);
break;
case ENCLOSER:
if (worm[player].encloser)
draw(x + 2, y, ENCLOSER);
else draw(x + 2, y, BLACKENED);
break;
case PUSHER:
if (worm[player].pusher)
draw(x, y + 1, PUSHER);
else draw(x, y + 1, BLACKENED);
break;
case REMNANTS:
if (worm[player].remnants)
draw(x + 1, y + 1, REMNANTS);
else draw(x + 1, y + 1, BLACKENED);
break;
case SIDESHOT:
if (worm[player].sideshot)
draw(x + 2, y + 1, SIDESHOT);
else draw(x + 2, y + 1, BLACKENED);
break;
case ARMOUR:
if (worm[player].armour)
{ if (worm[player].armour < 10)
{ if ((r % 4) <= 1)
{ draw(x, y + 2, ARMOUR);
} else draw(x, y + 2, BLACKENED);
} else draw(x, y + 2, ARMOUR);
} else draw(x, y + 2, BLACKENED);
break;
case CUTTER:
if (worm[player].cutter)
{ if (worm[player].cutter < 10)
{ if ((r % 4) <= 1)
{ draw(x + 1, y + 2, CUTTER);
} else draw(x + 1, y + 2, BLACKENED);
} else draw(x + 1, y + 2, CUTTER);
} else draw(x + 1, y + 2, BLACKENED);
break;
case GLOW:
if (worm[player].glow)
{ if (worm[player].glow < 10)
{ if ((r % 4) <= 1)
{ draw(x + 2, y + 2, GLOW);
} else draw(x + 2, y + 2, BLACKENED);
} else draw(x + 2, y + 2, GLOW);
} else draw(x + 2, y + 2, BLACKENED);
break;
default:
break;
} }
AGLOBAL void drawsquare(SBYTE x, SBYTE y)
{ if (field[x][y] >= FIRSTCREATURE)
{ drawcreature(whichcreature(x, y, field[x][y], 255));
} elif (field[x][y] >= FIRSTHEAD && field[x][y] <= LASTHEAD)
{ drawhead(field[x][y] - FIRSTHEAD);
} elif (field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL)
{ drawtail(x, y, tailfield[x][y] + (SECONDMAGIC * (field[x][y] - FIRSTTAIL)));
} elif (field[x][y] >= FIRSTGLOW && field[x][y] <= LASTGLOW)
{ drawtail(x, y, tailfield[x][y] + (SECONDMAGIC * (field[x][y] - FIRSTGLOW)));
} else
{ draw(x, y, field[x][y]);
} }
AGLOBAL void updatearrow(SBYTE arrowy)
{ SBYTE i, var = -1;
/* var of:
-2 : many there
-1 : nothing there
0-3 : just that worm,
FIRSTNUMBER-LASTNUMBER: just that number */
for (i = 0; i <= 3; i++)
{ if
( worm[i].control != NONE
&& worm[i].y == arrowy
&& (worm[i].lives || field[worm[i].x][worm[i].y] == i - FIRSTGRAVE)
)
{ if (var == -1)
{ var = i;
} else var = -2;
} }
if (numbery == arrowy)
if (var == -1)
var = FIRSTNUMBER + number - 1;
else var = -2;
if (var == -2)
draw(ARROWX, arrowy, ALL);
elif (var == -1 || var == LASTNUMBER + 1) // var may equal LASTNUMBER + 1 at end of level
draw(ARROWX, arrowy, BLACKARROW);
elif (var >= FIRSTNUMBER && var <= LASTNUMBER)
draw(ARROWX, arrowy, var);
else
{ // assert(var >= 0 && var <= 3);
if (worm[var].lives)
draw(ARROWX, arrowy, FIRSTARROW + var);
else draw(ARROWX, arrowy, FIRSTGRAVE + var);
} }
AGLOBAL void __inline bangdynamite(SBYTE x, SBYTE y, SBYTE player)
{ SBYTE xx, yy;
// Infects (turns to bang-dynamite) all surrounding dynamite.
for (xx = x - 1; xx <= x + 1; xx++)
{ for (yy = y - 1; yy <= y + 1; yy++)
{ if (valid(xx, yy))
{ if (field[xx][yy] == DYNAMITE)
{ field[xx][yy] = TEMPBANGDYNAMITE;
infector[xx][yy] = player;
} } } } }
AGLOBAL ULONG arand(ULONG number)
{ ULONG result;
result = (ULONG) (rand() % (number + 1));
return(result);
}
/* NAME align -- right-justify a string within another string
SYNOPSIS align(STRPTR, SBYTE, TEXT);
FUNCTION Moves all text in a string to the right, padding with
spaces. Does not itself add a null terminator.
INPUTS string - pointer to the string of text
size - size in characters of the containing string
filler - what to pad the left of the string with
NOTE Null terminators are written over by this function, but that
does not matter, because calling functions use Text() with an
explicit length. This function only works with monospaced
fonts. */
AGLOBAL void align(STRPTR string, SBYTE size, TEXT filler)
{ SBYTE i, shift, length;
length = strlen((const char*) string);
shift = size - length;
for (i = 1; i <= length; i++)
*(string + size - i) = *(string + size - i - shift);
for (i = 0; i <= shift - 1; i++)
*(string + i) = filler;
}
MODULE void ReadGameports(void)
{ ReadStandardJoystick(0);
ReadStandardJoystick(1);
ReadAdapterJoystick(2);
ReadAdapterJoystick(3);
ReadGamepads();
}
AGLOBAL void checkrectangle(SBYTE direction, SBYTE player, SBYTE horizontalsize, SBYTE verticalsize)
{ AUTO SBYTE i, x, y, leftx, rightx, topy, bottomy;
AUTO UBYTE c;
PERSIST struct
{ SBYTE deltax[4], deltay[4];
} deltas[4] =
{ { { 0, -1, 0, 1}, // northwest
{-1, 0, 1, 0}
},
{ { 0, 1, 0, -1}, // northeast
{-1, 0, 1, 0}
},
{ { 0, 1, 0, -1}, // southeast
{ 1, 0, -1, 0}
},
{ { 0, -1, 0, 1}, // southwest
{ 1, 0, -1, 0}
} };
PERSIST struct
{ SBYTE leftx, rightx, topy, bottomy;
} enclose[4] =
{ {-127, -1, -127, -1 }, // northwest
{ 1, 127, -127, -1 }, // northeast
{ 1, 127, 1, 127 }, // southeast
{-127, -1, 1, 127 } // southwest
};
x = worm[player].x;
y = worm[player].y; // for speed
for (i = 0; i <= verticalsize; i++)
{ x += deltas[direction].deltax[0];
y += deltas[direction].deltay[0];
if
( (!valid(x, y))
|| ( field[x][y] != FIRSTTAIL + player
&& field[x][y] != FIRSTGLOW + player
) )
{ return;
} }
for (i = 0; i <= horizontalsize; i++)
{ x += deltas[direction].deltax[1];
y += deltas[direction].deltay[1];
if
( (!valid(x, y))
|| ( field[x][y] != FIRSTTAIL + player
&& field[x][y] != FIRSTGLOW + player
) )
{ return;
} }
for (i = 0; i <= verticalsize; i++)
{ x += deltas[direction].deltax[2];
y += deltas[direction].deltay[2];
if
( (!valid(x, y))
|| ( field[x][y] != FIRSTTAIL + player
&& field[x][y] != FIRSTGLOW + player
) )
{ return;
} }
for (i = 0; i <= horizontalsize - 1; i++)
{ x += deltas[direction].deltax[3];
y += deltas[direction].deltay[3];
if
( (!valid(x, y))
|| ( field[x][y] != FIRSTTAIL + player
&& field[x][y] != FIRSTGLOW + player
) )
{ return;
} }
if (enclose[direction].leftx < -1)
enclose[direction].leftx = -horizontalsize;
elif (enclose[direction].leftx > 1)
enclose[direction].leftx = horizontalsize + 1;
if (enclose[direction].rightx < -1)
enclose[direction].rightx = -(horizontalsize + 1);
elif (enclose[direction].rightx > 1)
enclose[direction].rightx = horizontalsize;
if (enclose[direction].topy < -1)
enclose[direction].topy = -verticalsize;
elif (enclose[direction].topy > 1)
enclose[direction].topy = verticalsize + 1;
if (enclose[direction].bottomy < -1)
enclose[direction].bottomy = -(verticalsize + 1);
elif (enclose[direction].bottomy > 1)
enclose[direction].bottomy = verticalsize;
leftx = worm[player].x + enclose[direction].leftx;
rightx = worm[player].x + enclose[direction].rightx;
topy = worm[player].y + enclose[direction].topy;
bottomy = worm[player].y + enclose[direction].bottomy;
// assert(leftx >= 0 && rightx <= fieldx && topy >= 0 && bottomy <= fieldy && leftx < rightx && topy < bottomy);
for (x = leftx; x <= rightx; x++)
{ for (y = topy; y <= bottomy; y++)
{ c = field[x][y];
if
( (c >= FIRSTEMPTY && c <= LASTEMPTY)
|| ((c >= FIRSTTAIL && c <= LASTTAIL ) && c != FIRSTTAIL + player)
|| ((c >= FIRSTGLOW && c <= LASTGLOW ) && c != FIRSTGLOW + player)
)
{ change(x, y, FIRSTGLOW + player);
enclosed = TRUE;
} elif (c >= FIRSTCREATURE)
{ wormkillcreature(player, whichcreature(x, y, c, 255));
change(x, y, FIRSTFLOWER + player);
enclosed = TRUE;
} } }
if (enclosed)
{ effect(FXDO_ENCLOSE);
} }
MODULE void endoflevel(void)
{ SBYTE advancer,
player;
UWORD counter[4] = {0, 0, 0, 0};
SBYTE x, y;
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ if (field[x][y] == FIRSTTAIL + player || field[x][y] == FIRSTGLOW + player)
{ counter[player]++;
} } } } }
if (counter[0] >= counter[1]
&& counter[0] >= counter[2]
&& counter[0] >= counter[3])
{ advancer = 0;
} elif (counter[1] >= counter[0]
&& counter[1] >= counter[2]
&& counter[1] >= counter[3])
{ advancer = 1;
} elif (counter[2] >= counter[0]
&& counter[2] >= counter[1]
&& counter[2] >= counter[3])
{ advancer = 2;
} else
{ advancer = 3;
}
if (level++ == 0)
{ level = reallevel + 1;
reallevel = 0;
}
stopfx();
newlevel(advancer);
}
AGLOBAL FLAG getnumber(SBYTE player)
{ FLAG done;
SBYTE x, y;
/* This function returns TRUE if the final number (ie. 9) was gotten,
otherwise FALSE.
The calling function is responsible for calling putnumber() if it
wants a new number to appear. */
effect(FXDING);
wormscore(player, POINTS_LETTER * number);
worm[player].numbers++;
number++;
if (number == 10)
{ endoflevel();
return(TRUE);
} // else
do
{ done = findempty(&x, &y, FALSE);
} while (!done);
change(x, y, FIRSTCHERRY + player);
return(FALSE);
}
AGLOBAL ULONG wormobject(UBYTE player, SBYTE x, SBYTE y)
{ AUTO UBYTE c = field[x][y], d;
AUTO ULONG score = object[c].score;
AUTO UBYTE i, j, generated = 0;
AUTO SBYTE xx, xxx, yy, yyy;
AUTO ABOOL done;
PERSIST UBYTE otherfield[MAXFIELDX + 1][MAXFIELDY + 1];
for (i = 0; i <= MAGNETS; i++)
if (magnet[i].alive && x == magnet[i].x && y == magnet[i].y)
magnet[i].alive = FALSE;
if (!valid(x, y)) // defensive programming
{ return 0;
/* AUTO TEXT temp1[SAYLIMIT + 1], temp2[8];
strcpy(temp1, "BAD OBJECT AT x: ");
stci_d(temp2, x);
strcat(temp1, temp2);
strcat(temp1, ", y: ");
stci_d(temp2, y);
strcat(temp1, temp2);
strcat(temp1, "!");
say(temp1, worm[player].colour);
draw(fieldx + 1, 0, c); // indicates which object
Delay(250);
clearkybd();
anykey(FALSE); */
}
switch(c)
{
case BONUS:
if (level != 0)
{ getnumber(player);
change(numberx, numbery, FIRSTNUMBER + number - 1);
updatearrow(numbery);
}
break;
case AMMO:
effect(FXGET_AMMO);
worm[player].ammo += ADD_AMMO;
stat(player, AMMO);
break;
case ARMOUR:
effect(FXGET_OBJECT);
worm[player].armour += ADD_ARMOUR + arand(RAND_ARMOUR);
if (worm[player].armour > ARMOURLIMIT)
{ worm[player].armour = ARMOURLIMIT;
}
icon(player, ARMOUR);
break;
case BRAKES:
effect(FXGET_OBJECT);
worm[player].brakes = TRUE;
stat(player, BRAKES);
break;
case MINIBOMB:
drawhead(player);
bombblast(HEAD, player, worm[player].x, worm[player].y, FALSE);
break;
case SUPERBOMB:
drawhead(player);
bombblast(HEAD, player, worm[player].x, worm[player].y, TRUE);
break;
case POWER:
effect(FXGET_POWERUP);
if (worm[player].power < POWERLIMIT)
{ worm[player].power += 2;
stat(player, POWER);
}
break;
case SLAYER:
for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive)
{ if (creature[i].player != player)
{ wormkillcreature(player, i);
change(creature[i].x, creature[i].y, EMPTY);
} } }
for (i = 0; i <= 3; i++)
{ if (player != i && worm[i].armour == 0)
{ worm[i].alive = FALSE;
worm[i].cause = SLAYER;
} }
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
if (field[x][y] == SLIME)
change(x, y, DYNAMITE);
break;
case PROTECTOR:
// create protector
done = FALSE;
for (i = 0; i <= PROTECTORS; i++)
{ if (!protector[player][i].alive && !done)
{ do
{ protector[player][i].relx = (arand(1) * 2) - 1;
protector[player][i].rely = (arand(1) * 2) - 1;
for (j = 0; j <= PROTECTORS; j++)
{ if
( i == NOSE
|| !protector[player][j].alive
|| protector[player][j].x != xwrap(worm[player].x + protector[player][i].relx)
|| protector[player][j].y != ywrap(worm[player].y + protector[player][i].rely)
) // if we can find an area without a preexisting protector
{ effect(FXBORN_PROTECTOR);
done = TRUE;
protector[player][i].alive = TRUE;
protector[player][i].visible = FALSE;
protector[player][i].last = EMPTY;
if (i == NOSE)
{ worm[player].position = -1;
} } } }
while (!done);
} }
break;
case MISSILE_O:
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ createcreature(MISSILE_C, i, worm[player].x, worm[player].y, 0, 0, player, 0);
break;
} }
break;
case MINIHEALER:
effect(FXGET_OBJECT);
worm[player].lives += ADD_MINIHEALER;
stat(player, MINIHEALER);
break;
case MULTIPLIER:
effect(FXGET_OBJECT);
if (worm[player].multi < MULTILIMIT)
worm[player].multi *= 2;
break;
case ICE:
effect(FXGET_OBJECT);
ice += ADD_ICE + arand(RAND_ICE);
break;
case GROWER:
effect(FXGET_GROWER);
/* grow dynamite */
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
if (field[x][y] == DYNAMITE)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY)
field[xx][yy] = TEMPDYNAMITE;
/* grow silver */
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
if (field[x][y] == SILVER)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPDYNAMITE)
field[xx][yy] = TEMPSILVER;
/* grow gold */
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
if (field[x][y] == GOLD)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPDYNAMITE || field[xx][yy] == TEMPSILVER)
field[xx][yy] = TEMPGOLD;
/* update field */
for (x = 0; x <= fieldx; x++)
for (y = 0; y <= fieldy; y++)
switch (field[x][y])
{
case TEMPGOLD:
change(x, y, GOLD);
break;
case TEMPSILVER:
change(x, y, SILVER);
break;
case TEMPDYNAMITE:
change(x, y, DYNAMITE);
break;
default:
break;
}
break;
case TREASURE:
treasurer = player;
if (level)
{ secondsperlevel = 0;
say("Bonus Level", worm[treasurer].colour);
}
secondsperlevel += ADD_TREASURE + arand(RAND_TREASURE);
if (secondsperlevel > TIMELIMIT)
{ secondsperlevel = TIMELIMIT;
}
if (level)
{ stat(player, BONUS);
reallevel = level;
level = 0;
newlevel(player);
}
break;
case AFFIXER:
effect(FXGET_OBJECT);
worm[player].affixer = TRUE;
icon(player, AFFIXER);
break;
case SWITCHER:
effect(FXGET_OBJECT);
for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ if (field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL)
{ if (field[x][y] == FIRSTTAIL + player)
{ field[x][y] = FIRSTGLOW + player;
drawtail(x, y, tailfield[x][y] + (player * SECONDMAGIC) + FIRSTMAGIC);
} else
{ field[x][y] = FIRSTTAIL + player;
drawtail(x, y, tailfield[x][y] + (player * SECONDMAGIC));
} }
elif (field[x][y] >= FIRSTGLOW && field[x][y] <= LASTGLOW && field[x][y] != FIRSTGLOW + player)
{ field[x][y] = FIRSTGLOW + player;
drawtail(x, y, tailfield[x][y] + (player * SECONDMAGIC));
} elif (field[x][y] == SLIME)
{ change(x, y, DYNAMITE);
} } }
break;
case SUPERHEALER:
effect(FXGET_OBJECT);
worm[player].lives += ADD_SUPERHEALER;
stat(player, MINIHEALER);
break;
case UMBRELLA:
level += arand(1) + 1;
if (level >= levels)
level = levels;
endoflevel();
break;
case AUTOJUMP:
effect(FXGET_OBJECT);
worm[player].autojump = TRUE;
icon(player, AUTOJUMP);
break;
case SLOWER:
effect(FXGET_OBJECT);
for (i = 0; i <= CREATURES; i++)
{ if
( creature[i].alive
&& creature[i].species != MISSILE_C
)
{ creature[i].speed = slowdown(creature[i].speed, FALSE, FALSE);
} }
break;
case PULSE:
effect(FXBORN_FRAGMENT);
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive && generated <= 7)
{ switch (generated)
{
case 0:
xx = 0;
yy = -1;
break;
case 1:
xx = 1;
yy = -1;
break;
case 2:
xx = 1;
yy = 0;
break;
case 3:
xx = 1;
yy = 1;
break;
case 4:
xx = 0;
yy = 1;
break;
case 5:
xx = -1;
yy = 1;
break;
case 6:
xx = -1;
yy = 0;
break;
case 7:
xx = -1;
yy = -1;
break;
default:
// assert(0);
xx = yy = 0; // to avoid spurious warnings
break;
}
generated++;
if (valid(x + xx, y + yy) && (xx != worm[player].deltax || yy != worm[player].deltay))
{ d = field[x + xx][y + yy];
if
( (d >= FIRSTEMPTY && d <= LASTEMPTY)
|| (d >= FIRSTTAIL && d <= LASTTAIL )
|| d <= LASTOBJECT
)
{ createcreature(FRAGMENT, i, x + xx, y + yy, xx, yy, 255, FRAGMENT);
} } } }
break;
case REMNANTS:
effect(FXGET_OBJECT);
worm[player].remnants = TRUE;
icon(player, REMNANTS);
break;
case SIDESHOT:
effect(FXGET_POWERUP);
worm[player].sideshot = TRUE;
icon(player, SIDESHOT);
break;
case MAGNET:
effect(FXGET_OBJECT);
i = 0;
field[x][y] = EMPTY; // so that the magnet itself is destroyed
for (xx = 0; xx <= fieldx; xx++)
for (yy = 0; yy <= fieldy; yy++)
if (field[xx][yy] <= LASTOBJECT)
{ while (magnet[i].alive && i < MAGNETS)
i++;
if (i > MAGNETS)
{ break;
} else
{ magnet[i].x = xx;
magnet[i].y = yy;
magnet[i].object = field[xx][yy];
magnet[i].player = player;
magnet[i].alive = TRUE;
i++;
} }
break;
case CUTTER:
effect(FXGET_OBJECT);
worm[player].cutter += ADD_CUTTER + arand(RAND_CUTTER);
if (worm[player].cutter > CUTTERLIMIT)
{ worm[player].cutter = CUTTERLIMIT;
}
icon(player, CUTTER);
break;
case CYCLONE_O:
/* create cyclone */
do
{ x = arand(fieldx - 10) + 5;
y = arand(fieldy - 5) + 5;
d = field[x][y];
} while (d < FIRSTEMPTY || d > LASTEMPTY);
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ createcreature(CYCLONE_C, i, x, y, 0, 0, 255, 0);
break;
} }
break;
case LIGHTNING:
effect(FXGET_LIGHTNING);
for (xx = 0; xx <= fieldx; xx++)
{ for (yy = 0; yy <= fieldy; yy++)
{ otherfield[xx][yy] = EMPTY;
} }
for (xx = 0; xx <= fieldx; xx++)
{ for (yy = 0; yy <= fieldy; yy++)
{ if
( field[xx][yy] == FIRSTTAIL + player
|| field[xx][yy] == FIRSTGLOW + player
)
{ for (xxx = xx - 1; xxx <= xx + 1; xxx++)
{ for (yyy = yy - 1; yyy <= yy + 1; yyy++)
{ if (valid(xxx, yyy))
{ d = field[xxx][yyy];
if
( (d >= FIRSTTAIL && d <= LASTTAIL && d != FIRSTTAIL + player)
|| (d >= FIRSTGLOW && d <= LASTGLOW && d != FIRSTGLOW + player)
|| d == EMPTY
|| d <= LASTOBJECT
|| d >= FIRSTCREATURE
)
{ otherfield[xxx][yyy] = TEMPLIGHTNING;
if (anims)
{ draw(xxx, yyy, LIGHTNING);
} } } } } } } }
for (xx = 0; xx <= fieldx; xx++)
{ for (yy = 0; yy <= fieldy; yy++)
{ if (otherfield[xx][yy] == TEMPLIGHTNING)
{ d = field[xx][yy];
if (d >= FIRSTCREATURE)
{ i = whichcreature(xx, yy, d, 255);
if
( d == BIRD
|| d == DOG
|| player != creature[i].player
)
{ wormkillcreature(player, i);
change(xx, yy, EMPTY);
} }
else
{ change(xx, yy, EMPTY);
} } } }
break;
case PUSHER:
effect(FXGET_OBJECT);
worm[player].pusher = TRUE;
icon(player, PUSHER);
break;
case GLOW:
effect(FXGET_OBJECT);
worm[player].glow += ADD_GLOW + arand(RAND_GLOW);
if (worm[player].glow > GLOWLIMIT)
worm[player].glow = GLOWLIMIT;
icon(player, GLOW);
break;
case ENCLOSER:
effect(FXGET_OBJECT);
worm[player].encloser = TRUE;
icon(player, ENCLOSER);
break;
case CONVERTER:
effect(FXGET_OBJECT);
for (i = 0; i <= CREATURES; i++)
{ if
( creature[i].alive
&& creature[i].species == FRAGMENT
&& creature[i].subspecies != BANANA
)
{ xx = creature[i].x;
yy = creature[i].y;
creature[i].alive = FALSE;
change(xx, yy, EMPTY);
createcreature(MISSILE_C, i, xx, yy, 0, 0, player, 0);
} }
break;
default:
// assert(0);
break;
}
return(score);
}
AGLOBAL void wormcol(SBYTE player, SBYTE x, SBYTE y)
{ ABOOL flag;
UBYTE c = field[x][y], d;
SBYTE i, xx, yy;
if (c == EMPTY)
{ wormscore(player, POINTS_EMPTY);
} elif (c >= FIRSTHEAD && c <= LASTHEAD)
{ wormworm(x, y, player, c - FIRSTHEAD);
} elif (c == FROGTONGUE)
{ if (worm[player].armour == 0)
{ worm[player].cause = FROG;
worm[player].alive = FALSE;
} }
elif (c == ARROWUP)
{ worm[player].speed = speedup(worm[player].speed, TRUE);
worm[player].last = ARROWUP;
stat(player, BRAKES);
} elif (c == ARROWDOWN)
{ if (worm[player].speed < SLOW) // so that the worm cannot be involuntarily brought to a total stop
{ worm[player].speed = slowdown(worm[player].speed, TRUE, worm[player].brakes);
}
worm[player].last = ARROWDOWN;
stat(player, BRAKES);
} elif (c == FROST)
{ worm[player].frosted = TRUE;
worm[player].last = FROST;
} elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
{ protworm(x, y, c - FIRSTPROTECTOR, player);
} elif (c == START)
{ level++;
if (level >= levels)
level = levels;
endoflevel();
} elif
( c == STONE
|| c == METAL
|| c == WOOD
|| (c >= FIRSTCREATURE && creatureinfo[c - FIRSTCREATURE].wall)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
|| (c >= FIRSTGLOW && c <= LASTGLOW)
) // if you've hit something that is pushable
{ flag = TRUE; // flag is whether you deserve to be in pain
if (worm[player].pusher)
{ xx = x + worm[player].deltax;
yy = y + worm[player].deltay;
if (valid(xx, yy))
{ d = field[xx][yy];
if
( d <= LASTOBJECT
|| (d >= FIRSTEMPTY && d <= LASTEMPTY)
)
// if pushing the square into a square which
// has an object or is empty/silver/gold
{ flag = FALSE; // then worm doesn't die
if (c >= FIRSTCREATURE)
{ // assert(creatureinfo[c - FIRSTCREATURE].wall);
i = whichcreature(x, y, c, 255);
creature[i].x = xx;
creature[i].y = yy;
drawcreature(i);
} elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ field[xx][yy] = c;
drawtail(xx, yy, tailfield[x][y] + ((c - FIRSTTAIL) * SECONDMAGIC));
} elif (c >= FIRSTGLOW && c <= LASTGLOW)
{ field[xx][yy] = c;
drawtail(xx, yy, tailfield[x][y] + ((c - FIRSTGLOW) * SECONDMAGIC));
} else
{ change(xx, yy, c);
} } }
else // if pushing off the field edges
{ flag = FALSE; // then worm doesn't die
if (c >= FIRSTCREATURE && creatureinfo[c - FIRSTCREATURE].wall)
{ // assert(c <= LASTCREATURE);
wormkillcreature(player, whichcreature(x, y, c, 255));
} } }
if (flag) // if not pushing successfully
{ if
( (c >= FIRSTTAIL && c <= LASTTAIL)
|| (c >= FIRSTGLOW && c <= LASTGLOW)
)
{ if (worm[player].armour > 0)
{ if (players > 1)
{ if
( (c >= FIRSTTAIL && c <= LASTTAIL && player == c - FIRSTTAIL)
|| (c >= FIRSTGLOW && c <= LASTGLOW && player == c - FIRSTGLOW)
)
{ worm[player].last = SILVER;
} else
{ worm[player].last = GOLD;
} } }
elif (!enclosed)
{ if
( (c >= FIRSTTAIL && c <= LASTTAIL)
|| player != c - FIRSTGLOW
)
{ if (!checkautojump(player))
{ worm[player].cause = c;
worm[player].alive = FALSE;
if (c >= FIRSTGLOW && c <= LASTGLOW)
{ ramming(player);
} } } } }
elif (c >= FIRSTCREATURE)
{ // assert(creatureinfo[c - FIRSTCREATURE].wall);
if (!checkautojump(player))
{ i = whichcreature(x, y, c, 255);
wormcreature(player, i);
} }
else
{ if (!checkautojump(player))
{ worm[player].cause = c;
worm[player].alive = FALSE;
ramming(player);
} } } }
elif (c == SLIME)
{ if (worm[player].armour == 0)
{ worm[player].cause = c;
worm[player].alive = FALSE;
} }
elif (c >= FIRSTCREATURE)
{ // assert(!creatureinfo[c - FIRSTCREATURE].wall);
i = whichcreature(x, y, c, 255);
wormcreature(player, i);
} elif (c == TELEPORT)
{ i = whichteleport(x, y);
if (blockedtel(i, worm[player].deltax, worm[player].deltay))
{ worm[player].cause = TELEPORT;
worm[player].alive = FALSE;
ramming(player);
} else
{ effect(FXUSE_TELEPORT);
worm[player].x = xwrap(teleport[partner(i)].x + worm[player].deltax);
worm[player].y = ywrap(teleport[partner(i)].y + worm[player].deltay);
} }
elif (c >= FIRSTGLOW && c <= LASTGLOW)
{ if (player != c - FIRSTGLOW)
{ if (worm[player].armour == 0)
{ worm[player].cause = GLOW;
worm[player].alive = FALSE;
}
ramming(player);
} }
else bothcol(player, x, y);
}
AGLOBAL void drawhead(SBYTE player)
{ if (worm[player].alive)
{ if (worm[player].glow == 0)
{ realdrawhead(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
worm[player].flashed = FALSE;
} else
{ if (worm[player].glow < 10)
{ if (!worm[player].flashed)
{ realdrawhead(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
} else draw(worm[player].x, worm[player].y, WHITENED);
worm[player].flashed = !worm[player].flashed;
} else realdrawhead(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
} }
else draw(worm[player].x, worm[player].y, FIRSTPAIN + player);
}
AGLOBAL SBYTE whichteleport(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= 1; which++)
if (teleports && teleport[which].x == x && teleport[which].y == y)
return((SBYTE) which);
return((SBYTE) -1); /* error code */
}
AGLOBAL void enginerundown(SBYTE player)
{ SBYTE x, y;
UBYTE i, j,
multiply = worm[player].multi * players;
quantity[0][0] = level - 1;
quantity[0][1] = BONUS_LEVEL * multiply;
quantity[0][2] = quantity[0][0] * quantity[0][1];
quantity[1][0] = worm[player].numbers;
quantity[1][1] = BONUS_NUMBER * multiply;
quantity[1][2] = quantity[1][0] * quantity[1][1];
quantity[2][0] = secondsleft;
quantity[2][1] = BONUS_TIME * multiply;
quantity[2][2] = quantity[2][0] * quantity[2][1];
quantity[3][0] = 0;
for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ if (field[x][y] == FIRSTTAIL + player)
{ quantity[3][0]++;
} } }
quantity[3][1] = BONUS_TAIL * multiply;
quantity[3][2] = quantity[3][0] * quantity[3][1];
quantity[4][0] = 0;
for (x = 0; x <= fieldx; x++)
{ for (y = 0; y <= fieldy; y++)
{ if (field[x][y] == FIRSTGLOW + player)
{ quantity[4][0]++;
} } }
quantity[4][1] = BONUS_GLOW * multiply;
quantity[4][2] = quantity[4][0] * quantity[4][1];
clearscreen();
for (i = 0; i <= 3; i++)
if (worm[i].control != NONE)
for (j = 0; j <= LASTOBJECT; j++)
stat(i, j);
systemrundown(player, multiply); // control returns when it's time to continue
clearkybd();
effect(FXRIFF);
for (i = 0; i <= 3; i++)
{ if (worm[i].control != NONE && worm[i].control != AMIGA)
{ anykey(FALSE);
break;
} } }
void stat(SBYTE player, SBYTE line)
{ ABOOL print = TRUE;
SBYTE i, theline;
strcpy(stattext, " "); /* 7 spaces */
switch (line)
{
case BONUS:
while (worm[player].score >= worm[player].nextlife)
{ worm[player].lives++;
worm[player].nextlife += LIFEMODULO;
stat(player, MINIHEALER);
};
if (worm[player].multi > 1)
whiteline();
else colourline(player);
stcl_d(stattext, worm[player].score);
theline = 0;
for (i = 0; i <= 6; i++)
if (!stattext[i])
stattext[i] = ' ';
// score can't reach 10 million
break;
case MINIHEALER:
if (worm[player].lives > LIVESLIMIT)
worm[player].lives = LIVESLIMIT;
colourline(player);
stci_d(stattext, worm[player].lives);
for (i = 0; i <= 6; i++)
if (!stattext[i])
stattext[i] = ' ';
theline = 1;
break;
case BRAKES:
if (worm[player].brakes)
whiteline();
else colourline(player);
if (worm[player].speed == FAST)
strcpy(stattext, "Fast ");
elif (worm[player].speed == NORMAL)
strcpy(stattext, "Normal ");
elif (worm[player].speed == SLOW)
strcpy(stattext, "Slow ");
else
{ // assert(worm[player].brakes && worm[player].speed == 0);
strcpy(stattext, "Stopped");
}
theline = 2;
break;
case AMMO:
if (worm[player].ammo)
{ if (worm[player].ammo > AMMOLIMIT)
worm[player].ammo = AMMOLIMIT;
whiteline();
} else colourline(player);
stci_d(stattext, worm[player].ammo);
for (i = 0; i <= 6; i++)
if (!stattext[i])
stattext[i] = ' ';
theline = 3;
break;
case POWER:
switch(worm[player].power)
{
case 0:
colourline(player);
strcpy(stattext, "Single ");
break;
case 2:
whiteline();
strcpy(stattext, "Triple ");
break;
case 4:
whiteline();
strcpy(stattext, "Quint. ");
break;
case 6:
whiteline();
strcpy(stattext, "Sept. ");
break;
default:
break;
}
theline = 4;
break;
default:
print = FALSE;
/* This next line is just to prevent spurious compiler
warnings about possibly uninitialized variables */
theline = 0;
break;
}
/* Sometimes stat() is called with a valid line, yet an invalid player.
This is not a problem. */
if (print)
{ printstat(player, theline);
} }
MODULE FLAG checkautojump(SBYTE player)
{ SBYTE distance, xx, yy;
if (worm[player].autojump)
{ if (worm[player].speed == FAST)
distance = DISTANCE_FAST - 1;
elif (worm[player].speed == NORMAL)
distance = DISTANCE_NORMAL - 1;
else
{ // assert(worm[player].speed == SLOW);
distance = DISTANCE_SLOW - 1;
}
xx = xwrap(worm[player].x + (worm[player].deltax * distance));
yy = ywrap(worm[player].y + (worm[player].deltay * distance));
if
( (field[xx][yy] >= FIRSTEMPTY && field[xx][yy] <= LASTEMPTY)
|| field[xx][yy] <= LASTOBJECT
)
{ worm[player].x = xx;
worm[player].y = yy;
wormcol(player, xx, yy);
return(TRUE);
} }
return(FALSE);
}